<?php
declare(strict_types=1);

namespace app\shop\logic;

use app\admin\model\shop\order\Order;
use app\shop\enums\goods\GoodsStockDeductType;
use app\shop\enums\notify\NotifyMessageType;
use app\shop\enums\order\OrderPayStatus;
use app\shop\enums\order\OrderStatus;
use app\shop\enums\payment\OrderType;
use app\shop\enums\payment\PayWay;
use app\shop\enums\payment\TradeState;
use app\shop\exception\FailException;
use app\shop\exception\ParamsErrorException;
use app\shop\job\ConfirmOrderPayStatusJob;
use app\shop\library\calculate\Calculate;
use app\shop\library\pay\PayProvider;
use app\shop\library\ShopAuth;
use app\shop\library\ShopHelper;
use app\shop\library\ShopLogHelper;
use app\shop\model\goods\GoodsSkuModel;
use app\shop\model\order\OrderAddressModel;
use app\shop\model\order\OrderDeliveryModel;
use app\shop\model\order\OrderDetailModel;
use app\shop\model\order\OrderModel;
use app\shop\model\order\StoreOrderModel;
use app\shop\model\PaymentTradeModel;
use app\shop\model\user\UserAddressModel;
use app\shop\model\user\UserCartModel;
use app\shop\model\user\UserModel;
use app\shop\service\CouponService;
use app\shop\service\DeliveryService;
use app\shop\service\LogService;
use app\shop\service\NotifyService;
use app\shop\service\OrderService;
use app\shop\service\PaymentTradeService;
use app\shop\service\UserAddressService;
use Exception;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Db;
use think\facade\Queue;
use think\helper\Arr;
use think\Model;
use think\Paginator;
use Throwable;

/**
 * @property UserModel $model
 */
class OrderLogic extends BaseLogic
{
    public function __construct()
    {
        $this->model = new UserModel();
    }

    protected int $userId = 0;

    /**
     * 列表
     * @param array $params
     * @return array|Paginator
     */
    public function list(array $params): Paginator|array
    {
        $query = OrderModel::getQuery();

        $this->buildEqualQuery($query, $params, [
            'user_id' => 'user_id',
            'type'    => 'type',
            'status'  => 'status',
        ]);

        $query->field([
            'id', 'status', 'pay_price', 'total_num', 'create_time', 'final_pay_time', 'refund_status',
            'out_trade_no', 'type' , 'play_game_name', 'play_region', 'play_user_id','play_mobile'
        ])->with([
            'detail'        => function ($query) {
                $query->field(['id', 'order_id', 'goods_name', 'price', 'num', 'image', 'spec']);
            },
            'payment_trade' => function ($query) {
                $query->field(['out_trade_no', 'trade_no', 'source']);
            }
        ]);

        $query->append(['countdown']);

        $query->order(['id' => 'desc']);

        $wxpayMichId = ShopHelper::get_setting('wxpay_mch_id');

        return OrderModel::paginateListQuery($query)->each(function ($item) use ($wxpayMichId) {
            if($item['type'] == 2){
                $num = 0;
                $playlist = Db::name('shop_order_play')->where('order_id',$item['id'])->select()->toArray();
                foreach ($playlist as $playk=>$play){
                    if($play['play_user_id']){
                        $play_user_info = Db::name('play_user')->where('id',$play['play_user_id'])->find();
                        // $playlist[$playk]['username'] = Db::name('shop_user')->where('id',$play_user_info['shop_user_id'])->value('nickname');
                        // $playlist[$playk]['avatar'] = Db::name('shop_user')->where('id',$play_user_info['shop_user_id'])->value('avatar');
                    }else{
                        $num += 1;
                    }
                };
                $item['play'] = ['user'=>$playlist,'num'=>$num,'play_status'=>Db::name('shop_order_play')->where('order_id',$item['id'])->where('user_id',$item['user_id'])->value('status')];
                if(isset($item['play_region']) && $item['play_region'] == 1){
                    $item['play_region'] = 'qq';
                }else if(isset($item['play_region']) && $item['play_region'] == 2){
                    $item['play_region'] = '微信';
                }
            }
            if ($item['payment_trade']) {
                $item['payment_trade']['wxpay_mch_id'] = $wxpayMichId;
            }
        });
    }


    public function storeorderlist(array $params): Paginator|array
    {

        $query = StoreOrderModel::getQuery();
        $where = [];
        if(isset($params['status']) && $params['status']){
            if($params['status'] == 1){
                $status = 10;
            }elseif($params['status'] == 2){
                $status = 20;
            }elseif($params['status'] == 3){
                $status = 21;
            }elseif($params['status'] == 4){
                $status = 30;
            }elseif($params['status'] == 5){
                $status = 100;
            }
            $where[] =  ['order_status', '=', $status];
        }
        $query->where($where);
        $this->buildEqualQuery($query, $params, [
            'user_id' => 'user_id',
        ]);
        $query->order(['id' => 'desc']);
        return StoreOrderModel::paginateListQuery($query)->each(function ($item) {
            $item->product_infos = json_decode($item->product_infos, true);
            $item->store_id = Db::name('store_store')->where('id',$item->store_id)->value('name');
            if($item->order_status == 10){
                $item->status = 1;
            }elseif($item->order_status == 20){
                $item->status = 2;
            }elseif($item->order_status == 21){
                $item->status = 3;
            }elseif($item->order_status == 30){
                $item->status = 4;
            }elseif($item->order_status == 100){
                $item->status = 5;
            }
        });
    }

    public function storeorderdetail(array $params): array
    {

        $query = StoreOrderModel::where([
            ['id', '=', $params['id']],
        ]);
        $result = $query
            ->findOrEmpty();

        if ($result->isEmpty()) {
            return [];
        }
        if(isset($result->product_infos)){
            $result->product_infos = json_decode($result->product_infos, true);
            $result->store_id = Db::name('store_store')->where('id',$result->store_id)->value('name');
        }
        return $result->toArray();
    }

    /**
     * 陪玩订单列表
     * @param array $params
     * @return array|Paginator
     */
    public function userPlaylist(array $params): Paginator|array
    {

        $query = OrderModel::getQuery();
        if(isset($params['status']) && $params['status']!=''){
            $playWhere['status'] = $params['status'];
            $playWhere['user_id'] = $params['user_id'];
        }else{
            $playWhere['user_id'] = $params['user_id'];
        }
        $this->userId = $params['user_id'];
        $orderid = Db::name('shop_order_play')->where($playWhere)->column('order_id');
        $query->where([
            ['id', 'in', $orderid],
        ]);
        $params['type'] = 2;
        unset($params['status']);
        $this->buildEqualQuery($query, $params, [
            'type'    => 'type',
        ]);

        $query->field([
            'id', 'status', 'pay_price', 'total_num', 'create_time', 'final_pay_time', 'refund_status',
            'out_trade_no', 'type' , 'play_game_name', 'play_region', 'play_user_id','play_mobile','user_id'
        ])->with([
            'detail'        => function ($query) {
                $query->field(['id', 'order_id', 'goods_name', 'price', 'num', 'image', 'spec']);
            },
            'payment_trade' => function ($query) {
                $query->field(['out_trade_no', 'trade_no', 'source']);
            }
        ]);

        $query->append(['countdown']);

        $query->order(['id' => 'desc']);

        $wxpayMichId = ShopHelper::get_setting('wxpay_mch_id');

        return OrderModel::paginateListQuery($query)->each(function ($item) use ($wxpayMichId) {
            if($item['type'] == 2){
                $num = 0;
                $playlist = Db::name('shop_order_play')->where('order_id',$item['id'])->select()->toArray();
                foreach ($playlist as $playk=>$play){

                    if($play['play_user_id']){
                        $play_user_info = Db::name('play_user')->where('id',$play['play_user_id'])->find();
                        $playlist[$playk]['username'] = Db::name('shop_user')->where('id',$play_user_info['shop_user_id'])->value('username');
                        $playlist[$playk]['avatar'] = Db::name('shop_user')->where('id',$play_user_info['shop_user_id'])->value('avatar');
                    }else{
                        $num += 1;
                    }
                };
                $item['play'] = ['user'=>$playlist,'num'=>$num,'play_status'=>Db::name('shop_order_play')->where('order_id',$item['id'])->where('user_id',$this->userId)->value('status')];
                if(isset($item['play_region']) && $item['play_region'] == 1){
                    $item['play_region'] = 'qq';
                }else if(isset($item['play_region']) && $item['play_region'] == 2){
                    $item['play_region'] = '微信';
                }
            }
            if ($item['payment_trade']) {
                $item['payment_trade']['wxpay_mch_id'] = $wxpayMichId;
            }
        });
    }

    public function toRobberyOrder(array $params)
    {
        $orderinfo = OrderModel::where('id', $params['id'])->find();
        if(!isset($orderinfo)){
            throw new ParamsErrorException('订单不存在');
        }
        $setting = Db::name('play_setting')->where('user_id', $params['user_id'])->find();
        if(!$setting){
            throw new ParamsErrorException('您未加入陪玩或未经审核');
        }
        if($setting['switch'] != 1){
            throw new ParamsErrorException('您已下线');
        }
        if(Db::name('shop_order_play')->where('order_id', $params['id'])->where('play_user_id',$setting['play_user_id'])->find()){
            throw new ParamsErrorException('您已经抢到订单名额');
        }
        $orderplay = Db::name('shop_order_play')->where('order_id', $params['id'])->where('play_user_id',0)->select()->toArray();
        if(count($orderplay) == 0){
            throw new ParamsErrorException('名额已满');
        }
        $data = [];
        $data['order_id'] = $params['id'];
        $data['play_user_id'] = $setting['play_user_id'];
        $data['user_id'] = $params['user_id'];
        $data['status'] = 2;
        $data['commission'] = number_format($orderinfo['total_price']*($setting['proportion']/100), 2);
        if($data['commission'] == 0){
            $data['commission'] = 0.01;
        }
        $data['update_time'] = time();
        $this->model->startTrans();
        try {
            $re = Db::name('shop_order_play')->where('id',$orderplay[0]['id'])->update($data);
            if($re){
                if(!Db::name('shop_order_play')->where('order_id', $params['id'])->where('play_user_id',0)->where('status','!=',2)->select()->toArray()){
                    OrderModel::where('id', $params['id'])->update(['status' => 3]);
                }
                $this->model->commit();
            }else{
                $this->model->rollback();
                throw new ParamsErrorException('抢单失败');
            }
        }catch (\Exception $e){
            $this->model->rollback();
            throw new FailException($e->getMessage(), null, $e, $params, true);
        }
        return ['id'=>$params['id']];

    }

    public function surePlayOrder(array $params)
    {
        $orderinfo = OrderModel::where('id', $params['id'])->find();
        if(!isset($orderinfo)){
            throw new ParamsErrorException('订单不存在');
        }
        $setting = Db::name('play_setting')->where('user_id', $params['user_id'])->find();
        if(!$setting){
            throw new ParamsErrorException('您未加入陪玩或未经审核');
        }
        if($setting['switch'] != 1){
            throw new ParamsErrorException('您已下线');
        }
        if(Db::name('shop_order_play')->where('order_id', $params['id'])->where('play_user_id',$setting['play_user_id'])->where('status',2)->find()){
            throw new ParamsErrorException('您已经确定接单');
        }
        $orderplay = Db::name('shop_order_play')->where('order_id', $params['id'])->where('play_user_id',$setting['play_user_id'])->where('status',4)->select()->toArray();
        if(!$orderplay){
            throw new ParamsErrorException('您的预约已取消，请重新抢单');
        }
        $data = [];
        $data['order_id'] = $params['id'];
        $data['play_user_id'] = $setting['play_user_id'];
        $data['user_id'] = $params['user_id'];
        $data['status'] = 2;
        $data['commission'] = number_format($orderinfo['total_price'] * ($setting['proportion']/100), 2);
        if($data['commission'] == 0){
            $data['commission'] = 0.01;
        }
        $data['update_time'] = time();
        $this->model->startTrans();
        try {
            $re = Db::name('shop_order_play')->where('id',$orderplay[0]['id'])->update($data);
            if($re){
                if(!Db::name('shop_order_play')->where('order_id', $params['id'])->where('play_user_id',0)->where('status','!=',2)->select()->toArray()){
                    OrderModel::where('id', $params['id'])->update(['status' => 3]);
                }
                $this->model->commit();
            }else{
                $this->model->rollback();
                throw new ParamsErrorException('确定失败');
            }
        }catch (\Exception $e){
            $this->model->rollback();
            throw new FailException($e->getMessage(), null, $e, $params, true);
        }
        return ['id'=>$params['id']];

    }

    /**
     * 订单详情
     * @param array $params
     * @return array
     * @throws ParamsErrorException
     */
    public function detail(array $params): array
    {
        $query = OrderModel::getQuery();
        $this->buildEqualQuery($query, $params, [
            'id'      => 'id',
            // 'user_id' => 'user_id'
        ]);

        $query->field([
            'id', 'order_no', 'out_trade_no', 'status', 'freight', 'pay_price', 'total_price', 'total_num',
            'create_time', 'pay_time', 'final_pay_time', 'discount_data', 'remark', 'receive_time','play_game_name','play_region','play_user_id','play_mobile'
        ]);

        $query->with([
            'detail'        => function ($query) {
                $query->field([
                    'id', 'order_id', 'goods_id', 'goods_name', 'price', 'pay_price', 'num', 'image',
                    'spec as spec_text', 'evaluate_time'
                ])->with([
                    'refund' => function ($query) {
                        $query->field([
                            'id', 'order_id', 'order_detail_id', 'refund_type', 'refund_num', 'refund_money',
                            'create_time', 'status'
                        ]);
                    }
                ])->append(['apply_refund']);
            },
            'address'       => function ($query) {
                $query->field(['order_id', 'name', 'phone', 'full_address']);
            },
            'delivery'      => function ($query) {
                $query->field(['order_id', 'delivery_no', 'delivery_company_id'])->with([
                    'delivery_company' => function ($query) {
                        $query->field(['id', 'name']);
                    }
                ]);
            },
            'payment_trade' => function ($query) {
                $query->field(['out_trade_no', 'trade_no', 'source']);
            }
        ]);

        $query->append(['countdown']);

        $result = $query->findOrEmpty();
        if($result->play_region == 1){
            $result->play_region = 'qq';
        }else if($result->play_region == 2){
            $result->play_region = '微信';
        }else{
            $result->play_region = '';
        }
        if($result->play_user_id){
            $play_id = explode(',', $result->play_user_id);
            $play_uid = Db::name('play_user')->whereIn('id', $play_id)->column('shop_user_id');
            $username = Db::name('shop_user')->whereIn('id', $play_uid)->column('nickname');
            $result->play_user_id = implode(',', $username);
        }
        if ($result->payment_trade) {
            $result->payment_trade->wxpay_mch_id = ShopHelper::get_setting('wxpay_mch_id');
        }

        if ($result->isEmpty()) {
            throw new ParamsErrorException('订单不存在或已被删除');
        }

        return $result->toArray();
    }

    /**
     * 取消订单
     * @param array $params
     * @return bool
     * @throws FailException
     */
    public function cancel(array $params): bool
    {
        // 超时取消
        if (Arr::get($params, 'time_out')) {
            $orderId = intval($params['id']);
            $result  = false;
            try {
                (new OrderService())->autoCancel($orderId);
                $result = true;
            } catch (ParamsErrorException $e) {
                ShopLogHelper::info('订单无需自动取消', ['order_id' => $orderId, 'msg' => $e->getMessage()]);
            } catch (Exception $e) {
                ShopLogHelper::error('订单自动取消失败', ['order_id' => $orderId], $e);
            }

            return $result;
        }

        $this->model->startTrans();
        try {
            $order = OrderModel::where([
                'id'      => $params['id'],
                'user_id' => $params['user_id']
            ])->findOrEmpty();
            if (!in_array($order->status, [OrderStatus::TO_PAT, OrderStatus::TO_SHIPPED, OrderStatus::TO_EVALUATE])) {
                throw new FailException('订单状态异常');
            }
            if ($order->status == OrderStatus::CANCEL) {
                throw new FailException('订单已被取消');
            }

            $order->cancel_reason = $params['cancel_reason'] ?: '用户申请取消';
            // 未支付订单直接取消
            if ($order->status == OrderStatus::TO_PAT) {
                (new OrderService())->cancelOrder([
                    'order_id'      => $order->id,
                    'cancel_reason' => $order->cancel_reason,
                    'refund_amount' => 0,
                    'operator_id'   => $params['user_id'],
                    'is_admin'      => false,
                ]);
            } else {
                // 已支付订单需审核
                LogService::orderStatusLog($order->id, OrderStatus::REFUND, '用户申请取消', $params['user_id']);

                $order->status = OrderStatus::REFUND;
                $order->save();

                (new NotifyService())->send('order_cancel', $order->toArray());
            }

            $this->model->commit();
        } catch (Throwable $e) {
            $this->model->rollback();
            throw new FailException($e->getMessage(), null, $e, $params, true);
        }

        return true;
    }

    /**
     * 删除订单
     * @param array $params
     * @return void
     * @throws FailException
     */
    public function delete(array $params): void
    {
        OrderModel::startTrans();
        try {
            OrderModel::where([
                'id'      => $params['id'],
                'user_id' => $params['user_id'],
            ])->update(['delete_time' => time(), 'user_del' => 1]);

        } catch (Throwable $e) {
            OrderModel::rollback();
            throw new FailException('删除失败', null, $e);
        }

        OrderModel::commit();
    }

    /**
     * 确认收货
     * @param array $params
     * @return OrderModel|array|mixed|Model
     * @throws FailException
     */
    public function receive(array $params): mixed
    {
        $order = OrderModel::where([
            'id'      => $params['id'],
            'user_id' => $params['user_id']
        ])->findOrEmpty();

        if ($order->status != OrderStatus::TO_RECEIVED) {
            throw new FailException('订单状态异常');
        }

        $orderService = new OrderService();
        OrderModel::startTrans();
        try {
            $order->status       = OrderStatus::TO_EVALUATE;
            $order->receive_time = time();

            $order->save();

            LogService::orderStatusLog($order->id, OrderStatus::TO_EVALUATE, '用户确认收货', $params['user_id']);
            if($order->type == 2){
                $orderModel = new Order();
                $success = $orderModel->OrderSuccess(['order_id' => $order->id]);
                if(!$success['status']){
                    throw new FailException($success['msg']);
                }
            }else{
                $orderService->afterReceive($order->id);
            }

            OrderModel::commit();
        } catch (Throwable $e) {
            OrderModel::rollback();
            throw new FailException('确认收货失败', null, $e, $order->toArray(), true);
        }

        return $order;
    }


    /**
     * 直接下单
     * @param array $params
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws FailException
     * @throws ModelNotFoundException
     */
    public function getPreByNow(array $params): array
    {
        $goods[] = $this->getGoodsSkuInfo($params['sku_id'], $params['num']);

        return $this->getPreData($goods, $params);
    }

    /**
     * 购物车下单
     * @param array $params
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException|FailException
     */
    public function getPreByCart(array $params): array
    {
        try {
            $cartInfo = UserCartModel::where([
                    'selected' => 1,
                    'user_id'  => $params['user_id']
                ]
            )->field(['sku_id', 'num'])->selectOrFail();
        } catch (DataNotFoundException|ModelNotFoundException) {
            throw new FailException('请选择购物车商品');
        }
        $goods = array_map(function ($cart) {
            try {
                $goodsSkuInfo = $this->getGoodsSkuInfo($cart['sku_id'], $cart['num']);
            } catch (Throwable $e) {
                throw new FailException($e->getMessage());
            }
            return $goodsSkuInfo;
        }, $cartInfo->toArray());

        //排除掉失效的商品
        $goods = array_filter($goods);

        return $this->getPreData($goods, $params);
    }

    /**
     * 再次购买下单
     * @param array $params
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws FailException
     */
    public function getPreByRepurchase(array $params): array
    {
        $order = OrderModel::where(['id' => $params['id']])
            ->with([
                'detail',
                'address'
            ])->findOrEmpty();

        if ($order->isEmpty()) {
            throw new FailException('订单不存在');
        }
        $goods = array_map(function ($detail) {
            try {
                $goodsSkuInfo = $this->getGoodsSkuInfo($detail['sku_id'], $detail['num']);
            } catch (Throwable $e) {
                throw new FailException($e->getMessage());
            }
            return $goodsSkuInfo;
        }, $order->detail->toArray());

        //排除掉失效的商品
        $goods = array_filter($goods);

        if (empty($goods)) {
            throw new FailException('该订单内商品均已下架,看看其他的吧');
        }

        return $this->getPreData($goods, $params);
    }

    /**
     * 获取购买商品sku信息
     * @param int|string|array $skuId 商品sku_id
     * @param int|string       $num   购买数量
     * @return array
     * @throws FailException
     */
    private function getGoodsSkuInfo(int|string|array $skuId, int|string $num): array
    {
        $goodsSku = GoodsSkuModel::where(['id' => $skuId])
            ->withoutField(['create_time', 'update_time', 'delete_time'])
            ->append(['spec_text'])
            ->hidden(['spec'])
            ->with([
                'goods',
                'template'
            ])->findOrEmpty();

        if ($goodsSku->isEmpty()) {
            throw new FailException('购买商品不存在或已被删除');
        }

        $goods = $goodsSku->goods;
        if ($goods['status'] != 1) {
            throw new FailException("商品【{$goods['name']}】已下架");
        }
        if ($goodsSku->stock < $num) {
            throw new FailException("商品【{$goods['name']}-{$goodsSku['spec_text']}】库存不足");
        }

        $goodsSku->num = $num;

        $goodsSku->total_weight = Calculate::newDecimal($goodsSku->weight)->mul($num, 3)->getValue();
        $goodsSku->total_volume = Calculate::newDecimal($goodsSku->volume)->mul($num, 3)->getValue();
        $goodsSku->total_price  = Calculate::newDecimal($goodsSku->price)->mul($num)->getValue();

        //商品实际支付价格
        $goodsSku->pay_price = $goodsSku->total_price;

        return $goodsSku->toArray();
    }

    /**
     * 下单前数据
     * @param array $goods
     * @param array $params
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    private function getPreData(array $goods, array $params): array
    {
        $addressId      = $params['address_id'] ?? '0';
        $couponId       = $params['coupon_id'] ?? '';
        $couponList     = [];
        $couponDiscount = '0';
        $freight        = '0';
        $error          = '';

        $totalPrice = array_reduce($goods, function ($carry, $item) {
            return Calculate::newDecimal($carry)->add($item['total_price'])->getValue();
        }, '0');
        $payPrice   = array_reduce($goods, function ($carry, $item) {
            return Calculate::newDecimal($carry)->add($item['pay_price'])->getValue();
        }, '0');
        // 运费
        try {
            $freight = DeliveryService::calculateFreight($goods, $addressId);
        } catch (Throwable $e) {
            $error = $e->getMessage();
        }
        // 优惠券折扣
        try {
            $couponDiscountInfo = CouponService::calculateCoupon($goods, (float)$totalPrice, $couponId);
            $couponList         = $couponDiscountInfo['coupon_list'];
            $couponDiscount     = $couponDiscountInfo['coupon_discount'];
        } catch (Throwable $e) {
            ShopLogHelper::error('优惠券计算异常', ['goods' => $goods, 'params' => $params], $e);
        }
        $totalDiscount = Calculate::newDecimal()->add($couponDiscount)->getValue();

        // 实际支付金额
        $payPrice = Calculate::newDecimal($payPrice)->add($freight)->sub($totalDiscount)->getValue();
        if (Calculate::newDecimal($payPrice)->compLte(0.01)) {
            $payPrice = '0.01';
        }

        $summary = array_merge([
            'total_num' => array_sum(array_column($goods, 'num')),
        ], array_map([ShopHelper::class, 'format_price'], [
            'total_price'     => $totalPrice,
            'freight'         => $freight,
            'coupon_discount' => $couponDiscount,
            'total_discount'  => $totalDiscount,
            'pay_price'       => $payPrice
        ]));

        return [
            'goods'        => $goods,
            'user_address' => UserAddressService::getAllAddress(),
            'summary'      => $summary,
            'coupon_list'  => $couponList,
            'error'        => $error,
        ];
    }

    /**
     * 创建订单
     * @param array $params
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws FailException
     * @throws ModelNotFoundException
     * @throws ParamsErrorException
     */
    public function create(array $params): array
    {
        $buyGoods    = [];
        $orderDetail = [];
        $addressId   = $params['address_id'] ?? 0;
        $userId      = $params['user_id'] ?? 0;
        $couponId    = $params['coupon_id'] ?? 0;
        if($params['type'] == 1){
            $address = UserAddressModel::where(['id' => $addressId])->findOrEmpty();
            if ($address->isEmpty()) {
                throw new ParamsErrorException('请先选择收货地址');
            }
            $address = $address->toArray();
        }else if($params['type'] == 2){
            $address = [];
            if(count($params['goods']) > 1){
                throw new ParamsErrorException('陪玩商品只支持单商品购买');
            }
        }else if($params['type'] == 3){
            throw new ParamsErrorException('暂无此类型订单');
        }

        foreach ($params['goods'] as $goods) {
            $goodsSku   = $this->getGoodsSkuInfo($goods['sku_id'], $goods['num']);
            $buyGoods[] = $goodsSku;
        }

        $preData = $this->getPreData($buyGoods, $params);
        if (!empty($preData['error'] ?? '')) {
            throw new FailException($preData['error']);
        }

        $summary = $preData['summary'];

        $orderService = new OrderService();
        $orderNo      = $orderService->generateOrderNo();
        $outTradeNo   = $orderService->generateOrderNo('SC');

        $autoCancelTime = $orderService->getAutoCancelTime();

        $orderData = [
            'type'           => $params['type'],
            'order_no'       => $orderNo,
            'out_trade_no'   => $outTradeNo,
            'user_id'        => $userId,
            'total_num'      => $summary['total_num'],
            'total_price'    => $summary['total_price'],
            'pay_price'      => $summary['pay_price'],
            'pay_way'        => PayWay::BALANCE,
            'freight'        => $summary['freight'],
            'couponId'       => $couponId,
            'total_discount' => $summary['total_discount'] ?? '0.00',
            'discount_data'  => [
                'coupon_discount' => $summary['coupon_discount'] ?? '0.00',
            ],
            'status'         => OrderStatus::TO_PAT,
            'pay_status'     => OrderPayStatus::UNPAID,
            'source'         => ShopAuth::instance()->getUserSourceClient(),
            'remark'         => $params['remark'],
            'final_pay_time' => time() + $autoCancelTime,
            'play_game_name' => isset($params['play_game_name']) ? $params['play_game_name'] : '',
            'play_region' => isset($params['play_region']) ? $params['play_region'] : '',
            'play_user_id' => isset($params['play_user_id']) ? $params['play_user_id'] : '',
            'play_mobile' => isset($params['play_mobile']) ? $params['play_mobile'] : '',
        ];

        foreach ($preData['goods'] as $goodsSku) {
            $orderDetail[] = [
                'goods_id'          => $goodsSku['goods']['id'],
                'deduct_stock_type' => $goodsSku['goods']['deduct_stock_type'],
                'goods_name'        => $goodsSku['goods']['name'],
                'sku_id'            => $goodsSku['id'],
                'image'             => $goodsSku['image'],
                'spec'              => $goodsSku['spec_text'],
                'price'             => $goodsSku['price'],
                'num'               => $goodsSku['num'],
                'pay_price'         => $goodsSku['pay_price'],
                'total_price'       => $goodsSku['total_price'],
                'discount_amount'   => $goodsSku['discount_amount'] ?? '0.00',
                'discount_data'     => [
                    'coupon_discount' => $goodsSku['coupon_discount'] ?? '0.00',
                ],
            ];
        }

        OrderModel::startTrans();
        try {
            //记录订单数据
            $order   = OrderModel::create($orderData);

            $orderId = $order->id;

            $this->saveOrderDetail($orderId, $orderDetail);
            $this->processStock($orderDetail);

            if($params['type'] != 2){
                $this->saveOrderAddress($orderId, $address);
            }

            LogService::orderStatusLog($orderId, OrderStatus::TO_PAT, '订单创建', $userId);

            $this->processUserCart($orderDetail);

            $this->processOrderCoupon($orderId, $couponId);

            // $orderService->addAutoCancelJob($orderId);

            OrderModel::commit();
        } catch (Throwable $e) {
            OrderModel::rollback();

            throw new FailException('订单创建失败', null, $e);
        }

        return ['id' => $orderId];
    }

    /**
     * 处理库存
     * @param array $detailData
     * @return void
     */
    private function processStock(array $detailData): void
    {
        foreach ($detailData as $detail) {
            if ($detail['deduct_stock_type'] == GoodsStockDeductType::AFTER_ORDER) {
                $goodsSkuModel = GoodsSkuModel::where('id', $detail['sku_id'])->with(['goods'])->lock(true)->findOrEmpty();
                $goodsSkuModel->dec('stock', floatval($detail['num']))->save();
                $goodsSkuModel->goods->dec('total_stock', floatval($detail['num']))->save();

                // if ($goodsSkuModel->stock <= $goodsSkuModel->stock_alert) {
                //     (new NotifyService())->send(NotifyMessageType::LOW_STOCK, $goodsSkuModel->toArray());
                // } elseif ($goodsSkuModel->goods->total_stock <= $goodsSkuModel->goods->stock_alert) {
                //     (new NotifyService())->send(NotifyMessageType::LOW_STOCK, $goodsSkuModel->goods->toArray());
                // }
            }
        }
    }

    /**
     * 保存订单详情
     * @param int|string $orderId
     * @param array      $detailData
     * @return void
     * @throws Exception
     */
    private function saveOrderDetail(int|string $orderId, array $detailData): void
    {
        foreach ($detailData as &$detail) {
            $detail['order_id'] = $orderId;
        }
        $orderDetailModel = new OrderDetailModel();
        $orderDetailModel->saveAll($detailData);
    }

    /**
     * 保存优惠详情数据
     * @param int|string $orderId
     * @param array      $discountData
     * @return void
     */
    private function saveDiscountDetail(int|string $orderId, array $discountData): void
    {
        OrderModel::where(['id' => $orderId])->update([
            'discount_detail' => ShopHelper::array_to_json($discountData),
        ]);
    }

    /**
     * 保存用户下单地址
     * @param int|string $orderId
     * @param array      $address
     * @return void
     */
    private function saveOrderAddress(int|string $orderId, array $address): void
    {
        OrderAddressModel::create([
            'order_id'      => $orderId,
            'user_id'       => ShopAuth::instance()->id,
            'name'          => $address['name'],
            'phone'         => $address['phone'],
            'province'      => $address['province'],
            'city'          => $address['city'],
            'county'        => $address['county'],
            'full_location' => $address['full_location'],
            'address'       => $address['address'],
            'full_address'  => implode(' ', [$address['full_location'], $address['address']])
        ]);
    }

    /**
     * 处理购物车中的商品
     * @param array $orderDetail
     * @return void
     */
    private function processUserCart(array $orderDetail): void
    {
        $buySkuIds = array_column($orderDetail, 'sku_id');
        UserCartModel::where([
            'user_id' => ShopAuth::instance()->id,
            'sku_id'  => $buySkuIds,
        ])->delete();
    }


    /**
     * 处理订单优惠券
     * @param int|string $orderId
     * @param int|string $couponId
     * @throws FailException
     */
    function processOrderCoupon(int|string $orderId, int|string $couponId): void
    {
        if ($couponId) {
            //处理订单优惠券
            try {
                $couponInfo = (new CouponService())->consumeByOrderId($couponId);
            } catch (Exception $e) {
                throw new FailException($e->getMessage(), null, $e);
            }
            //记录订单优惠详情
            $this->saveDiscountDetail($orderId, [
                'coupon_discount' => $couponInfo
            ]);
        }
    }

    /**
     * 订单支付
     * @param array $params
     * @return array
     * @throws FailException
     * @throws ParamsErrorException
     */
    public function pay(array $params): array
    {
        $orderId         = $params['id'] ?? '';
        $payWay          = $params['pay_way'] ?? '';
        $isThirdPartyPay = ShopHelper::is_third_party_payment($payWay);

        $order = (new OrderService())->beforePay([
            'order_id' => $orderId
        ]);
        if ($order->isEmpty()) {
            throw new FailException('订单不存在或已被删除');
        }
        if (time() > $order->final_pay_time) {
            throw new FailException('订单已超时无法支付,请重新下单');
        }

        if ($isThirdPartyPay) {
            $paymentTradeModel = new PaymentTradeModel();
            $trade             = $paymentTradeModel->where('out_trade_no', $order->out_trade_no)->findOrEmpty();
            if (!$trade->isEmpty()) {
                $trade->delete();
            }
        }

        $order->startTrans();
        try {
            $payProvider = new PayProvider($payWay);
            $result      = $payProvider->pay([
                'user_id'      => $params['user_id'],
                'out_trade_no' => $order->out_trade_no,
                'amount'       => $order->pay_price,
                'desc'         => '商城订单',
                'source'       => ShopAuth::instance()->getUserSourceClient(),
                'quit_url'     => ShopHelper::full_h5_url('/pagesOrder/pages/list/list'),
                'return_url'   => ShopHelper::full_h5_url("/pagesOrder/pages/payment/payment?id=$order->id"),
            ]);

            $order->pay_way = $payWay;
            $order->save();

            //记录第三方支付信息
            if ($isThirdPartyPay) {
                $paymentTrade = PaymentTradeModel::create([
                    'order_type'   => OrderType::SHOP,
                    'order_id'     => $order->id,
                    'out_trade_no' => $order->out_trade_no,
                    'order_no'     => $order->order_no,
                    'user_id'      => $order->user_id,
                    'source'       => $order->source,
                    'pay_price'    => $order->pay_price,
                    'pay_way'      => $payWay,
                    'pay_params'   => is_array($result) ? ShopHelper::array_to_json($result) : null,
                    'status'       => TradeState::UNPAID,
                ]);

                Queue::later(30, ConfirmOrderPayStatusJob::class, ['payment_id' => $paymentTrade->id], ConfirmOrderPayStatusJob::$queue);
            }

            $order->commit();
        } catch (Throwable $e) {
            $order->rollback();
            throw new FailException($e->getMessage(), null, $e);
        }

        return $result;
    }


    /**
     * 支付后主动查询支付
     * @param int|string $orderId
     * @throws FailException
     */
    public function processNotify(int|string $orderId): void
    {
        $paymentTradeService = new PaymentTradeService();
        $payment             = $paymentTradeService->queryShopOrder($orderId);
        if ($payment->isEmpty()) {
            return;
        }
        if ($payment->trade_state == TradeState::PAID) {
            return;
        }
        $order = OrderModel::where('id', $payment->order_id)->findOrEmpty();
        if ($order->isEmpty()) {
            throw new FailException('订单异常,请返回列表刷新');
        }
        if ($order->status === OrderStatus::CANCEL) {
            throw new FailException('订单支付超时,请重新下单');
        }
    }

    /**
     * 支付后订单处理
     * @param int|string $orderId
     * @throws FailException
     */
    public function processShopOrder(int|string $orderId): void
    {
        $orderService = new OrderService();
        try {
            $orderService->afterPay($orderId);
        } catch (Throwable $e) {
            throw new FailException('订单支付异常：' . $e->getMessage(), null, $e);
        }
    }

    /**
     * 获取物流信息
     * @param int|string $orderId
     * @return array
     * @throws Exception
     */
    public function getDeliveryTracks(int|string $orderId): array
    {
        $query = OrderDeliveryModel::getQuery();

        $query->where('order_id', $orderId);

        $query->with([
            'detail'           => function ($query) {
                $query->field(['delivery_id', 'delivery_num', 'order_detail_id'])->with([
                    'order_detail' => function ($query) {
                        $query->field(['id', 'image']);
                    }
                ]);
            },
            'order_address'    => function ($query) {
                $query->field(['order_id', 'name', 'phone', 'full_address']);
            },
            'delivery_company' => function ($query) {
                $query->field(['id', 'name']);
            },
            'order_relation'   => function ($query) {
                $query->field(['id', 'user_id'])->with([
                    'detail' => function ($query) {
                        $query->field(['order_id', 'image']);
                    },
                    'user'   => function ($query) {
                        $query->field(['id', 'openid']);
                    },
                ]);
            },
        ]);

        $query->withoutField(['create_time']);

        $list = OrderDeliveryModel::listQuery($query);

        $deliveryService = new DeliveryService();
        foreach ($list as &$delivery) {
            $deliveryNo        = $delivery['delivery_no'];
            $userPhone         = $delivery['order_address']['phone'];
            $openid            = $delivery['order_relation']['user']['openid'];
            $deliveryCompanyId = $delivery['delivery_company']['id'] ?? '';
            if ($deliveryCompanyId) {
                $delivery['delivery_tracks'] = $deliveryService->getDeliveryTracks($deliveryNo, $deliveryCompanyId, $userPhone, $openid);
            } else {
                $delivery['delivery_tracks'] = [];
            }
        }

        return $list;
    }

    /**
     * @return string
     */
    function getStr(): string
    {
        return self::$status;
    }

}
